#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "des.h"

void Char8ToBit64(char ch[8], unsigned char bit[64]);
void Bit64ToChar8(unsigned char bit[64], char ch[8]);
void des_pc1_transform(unsigned char key[64], unsigned char temp[56]);
void des_pc2_transform(const unsigned char key[56], unsigned char temp[48]);
void des_ip_exchange(unsigned char data[64]);
void des_ip_1_exchange(unsigned char data[64]);
void des_e_transform(unsigned char data[48]);
void des_p_transform(unsigned char data[32]);
void des_rol(unsigned char data[56], int n);
void des_xor(unsigned char R[48], const unsigned char L[48], int count);
void des_swap(unsigned char left[32], unsigned char right[32]);
void des_sbox(unsigned char data[48]);
void des_generate_sub_key(unsigned char key[64], unsigned char subKeys[16][48]);
void des_block(char cipherBlock[8], unsigned char subKeys[16][48], char plainBlock[8], char mode);
void StringAppend(char *des, const char src[8]);
void substring(const char *src, int index, int len, char *target);

const int PC_1[56] = {56,48,40,32,24,16,8,  
              0,57,49,41,33,25,17,  
              9,1,58,50,42,34,26,  
              18,10,2,59,51,43,35,  
              62,54,46,38,30,22,14,  
              6,61,53,45,37,29,21,  
              13,5,60,52,44,36,28,  
              20,12,4,27,19,11,3};

const int PC_2[48] = {13,16,10,23,0,4,2,27,  
              14,5,20,9,22,18,11,3,  
              25,7,15,6,26,19,12,1,  
              40,51,30,36,46,54,29,39,  
              50,44,32,46,43,48,38,55,  
              33,52,45,41,49,35,28,31};

const int IP_Table[64] = {57,49,41,33,25,17,9,1,  
                        59,51,43,35,27,19,11,3,  
                        61,53,45,37,29,21,13,5,  
                        63,55,47,39,31,23,15,7,  
                        56,48,40,32,24,16,8,0,  
                        58,50,42,34,26,18,10,2,  
                        60,52,44,36,28,20,12,4,  
                        62,54,46,38,30,22,14,6};

const int IP_1_Table[64] =  {39,7,47,15,55,23,63,31,  
           38,6,46,14,54,22,62,30,  
           37,5,45,13,53,21,61,29,  
           36,4,44,12,52,20,60,28,  
           35,3,43,11,51,19,59,27,  
           34,2,42,10,50,18,58,26,  
           33,1,41,9,49,17,57,25,  
           32,0,40,8,48,16,56,24};

const int E_Table[48] =  {31, 0, 1, 2, 3, 4,  
                        3,  4, 5, 6, 7, 8,  
                        7,  8,9,10,11,12,  
                        11,12,13,14,15,16,  
                        15,16,17,18,19,20,  
                        19,20,21,22,23,24,  
                        23,24,25,26,27,28,  
                        27,28,29,30,31, 0};

const int P_Table[32] =    {15,6,19,20,28,11,27,16,  
                  0,14,22,25,4,17,30,9,  
                  1,7,23,13,31,26,2,8,  
                  18,12,29,5,21,10,3,24};

const int S[8][4][16] = 
            // S1
            {{{14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7},
			 {0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8},
			 {4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0},
			 {15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13}},
            // S2
            {{15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10},
			 {3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5},
			 {0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15},
			 {13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9}},
            // S3
            {{10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8},
			 {13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1},
			 {13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7},
			 {1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12}},
            // S4
            {{7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15},
			 {13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9},
			 {10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4},
			 {3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14}},
            // S5
            {{2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9},
			 {14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6},
			 {4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14},
			 {11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3}},
            // S6
            {{12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11},
			 {10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8},
			 {9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6},
			 {4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13}},
            // S7
            {{4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1},
			 {13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6},
			 {1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2},
			 {6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12}},
            // S8
            {{13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7},
			 {1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2},
			 {7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8},
			 {2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11}}};

const int MOVE_TIMES[16] = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1};

void ByteToBit(char ch, unsigned char bit[8])
{
    int i;
    for (i = 0; i < 8; i++)
        bit[7 - i] = (ch >> i) & 1;
}

void BitToByte(unsigned char bit[8], char *ch)
{
    int i;
    unsigned char tmp = 0;
    for (i = 0; i < 8; i++)
        tmp |= bit[i] << (7 -i);
    *ch = tmp;
}

void Char8ToBit64(char ch[8], unsigned char bit[64])
{
    int i;
    for (i = 0; i < 8; i++)
        ByteToBit(ch[i], bit + (i << 3));
}

void Bit64ToChar8(unsigned char bit[64], char ch[8])
{
    int i;
    for (i = 0; i < 8; i++)
        BitToByte(bit + (i << 3), ch + i);
}

void des_pc1_transform(unsigned char key[64], unsigned char temp[56])
{
    int i;
    for (i = 0; i < 56; i++)
        temp[i] = key[PC_1[i]];
}

void des_pc2_transform(const unsigned char key[56], unsigned char temp[48])
{
    int i;
    for (i = 0; i < 48; i++)
        temp[i] = key[PC_2[i]];
}

void des_ip_exchange(unsigned char data[64])
{
    int i;
    unsigned char temp[64];
    for (i = 0; i < 64; i++)
        temp[i] = data[IP_Table[i]];
    memcpy(data, temp, 64);
}

void des_ip_1_exchange(unsigned char data[64])
{
    int i;
    unsigned char temp[64];
    for (i = 0; i < 64; i++)
        temp[i] = data[IP_1_Table[i]];
    memcpy(data, temp, 64);
}

void des_e_transform(unsigned char data[48])
{
    int i;
    unsigned char temp[48];
    for (i = 0; i < 48; i++)
        temp[i] = data[E_Table[i]];
    memcpy(data, temp, 48);
}

void des_p_transform(unsigned char data[32])
{
    int i;
    unsigned char temp[32];
    for (i = 0; i < 32; i++)
        temp[i] = data[P_Table[i]];
    memcpy(data, temp, 32);
}

void des_rol(unsigned char data[56], int n)
{
    unsigned char temp[56];
    memcpy(temp, data, 56);

    memcpy(data, temp + n, 28 - n);
    memcpy(data + 28 - n, temp, n);

    memcpy(data + 28, temp + 28 + n, 28 - n);
    memcpy(data + 56 - n, temp + 28, n);
}

void des_xor(unsigned char R[48], const unsigned char L[48], int count)
{
    int i;
    for (i = 0; i < count; i++)
        R[i] ^= L[i];
}

void des_swap(unsigned char left[32], unsigned char right[32])
{
    unsigned char temp[32];
    memcpy(temp, left, 32);
    memcpy(left, right, 32);
    memcpy(right, temp, 32);
}

void des_sbox(unsigned char data[48])
{
    int i;
    int line, row, output;
    int cur1, cur2;
    for (i = 0; i < 8; i++) {
        cur1 = i * 6;
        cur2 = i << 2;

        line = (data[cur1]<<1) + data[cur1+5];
        row = (data[cur1+1]<<3) + (data[cur1+2]<<2)
            + (data[cur1+3]<<1) + (data[cur1+4]);
        output = S[i][line][row];

        // TO bit
        data[cur2] = (output&0x08)>>3;
        data[cur2+1] = (output&0x04)>>2;
        data[cur2+2] = (output&0x02)>>1;
        data[cur2+3] = output&0x01;
    }
}

void des_generate_sub_key(unsigned char key[64], unsigned char subKeys[16][48])
{
    unsigned char temp[56];
    int i;
    des_pc1_transform(key, temp);
    for (i = 0; i < 16; i++) {
        des_rol(temp, MOVE_TIMES[i]);
        des_pc2_transform(temp, subKeys[i]);
    }
}

//加密解密单个分组  
void des_block(char plainBlock[8], unsigned char subKeys[16][48], char cipherBlock[8], char mode)
{  
    unsigned char plainBits[64];  
    unsigned char copyRight[48];  
    int cnt, i;  
  
    Char8ToBit64(plainBlock,plainBits);       
    //初始置换（IP置换）  
    des_ip_exchange(plainBits);  
    //16轮迭代  
    for(cnt = 0; cnt < 16; cnt++) {
        i = mode == 'e' ? cnt : 15 - cnt;
        memcpy(copyRight,plainBits+32,32);  
        //将右半部分进行扩展置换，从32位扩展到48位  
        des_e_transform(copyRight);  
        //将右半部分与子密钥进行异或操作  
        des_xor(copyRight,subKeys[i],48);   
        //异或结果进入S盒，输出32位结果  
        des_sbox(copyRight);  
        //P置换  
        des_p_transform(copyRight);  
        //将明文左半部分与右半部分进行异或  
        des_xor(plainBits,copyRight,32);  
        if(cnt != 15){  
            //最终完成左右部的交换  
            des_swap(plainBits,plainBits+32);  
        }  
    }  
    //逆初始置换（IP^1置换）  
    des_ip_1_exchange(plainBits);  
    Bit64ToChar8(plainBits,cipherBlock);  
}  
  
void des_file(char *input_file, char desKey[8], char *output_file, char mode)
{
    FILE *input, *output;
    if((input = fopen(input_file,"rb")) == NULL){  
        printf("Failed to open inputfile.\n");
        return;  
    }     
    if((output = fopen(output_file,"wb")) == NULL){  
        printf("Failed to open outputfile.\n");
        return;  
    }  

    char data_block[8], processed_block[8];
    unsigned char subKeys[16][48], bKey[64];  
    long file_size, number_of_blocks;
    int padding;
    Char8ToBit64(desKey,bKey);  
    des_generate_sub_key(bKey, subKeys);

    fseek(input,0L,SEEK_END);   //将文件指针置尾  
    file_size = ftell(input);   //取文件指针当前位置  
    fseek(input, 0L, SEEK_SET); //将文件指针重指向文件头  
    number_of_blocks = file_size / 8 + (file_size % 8 ? 1 : 0);

    while (fread(data_block, 1, 8, input)) {
        number_of_blocks--;
        if (number_of_blocks == 0) {
            if (mode == 'e') {
                padding = 8 - file_size % 8;
                if (padding < 8) {
                    memset(data_block + 8 - padding, 0, padding);
                    data_block[7] = padding;
                }
                des_block(data_block, subKeys, processed_block, mode);
                fwrite(processed_block, 1, 8, output);
            } else {
                des_block(data_block, subKeys, processed_block, mode);
                padding = processed_block[7];
                if (padding < 8)
                    fwrite(processed_block, 1, 8 - padding, output);
                else
                    fwrite(processed_block, 1, 8, output);
            }
        } else {
            des_block(data_block, subKeys, processed_block, mode);
            fwrite(processed_block, 1, 8, output);
        }
        memset(data_block, 0, 8);
    }
    fclose(input);
    fclose(output);
}

char *string_encode(const char *str, char desKey[8])
{
    char *s;
    int i, padding, string_size, number_of_blocks;
    unsigned char bKey[64], subKeys[16][48];  
    char data_block[8], processed_block[8];
    Char8ToBit64(desKey,bKey);  
    des_generate_sub_key(bKey, subKeys);

    string_size = strlen(str);
    number_of_blocks = string_size / 8 + (string_size % 8 ? 1 : 0);
    s = (char *)malloc(number_of_blocks * 8 + 1);
    memset(s, 0, number_of_blocks * 8 + 1);
    for(i = 0; i < number_of_blocks; i++) {
        substring(str, i * 8, 8, data_block);
        if(i == number_of_blocks - 1) {
            padding = 8 - string_size % 8;
            if (padding < 8) {
                memset(data_block + 8 - padding, 0, padding);
                data_block[7] = padding;
            }
        }

        des_block(data_block, subKeys, processed_block, 'e');
        StringAppend(s, processed_block);
    }

    return s;
}

char *string_decode(const char *str, char desKey[8])
{
    char *s;
    int i, padding, string_size, number_of_blocks;
    unsigned char bKey[64], subKeys[16][48];  
    char data_block[8], processed_block[8];
    Char8ToBit64(desKey,bKey);  
    des_generate_sub_key(bKey, subKeys);

    string_size = strlen(str);
    number_of_blocks = string_size / 8 + (string_size % 8 ? 1 : 0);
    s = (char *)malloc(number_of_blocks * 8 + 1);
    memset(s, 0, number_of_blocks * 8 + 1);
    for (i = 0; i < number_of_blocks; i++) {
        substring(str, i * 8, 8, data_block);
        des_block(data_block, subKeys, processed_block, 'd');

        if(i == number_of_blocks - 1) {
            padding = processed_block[7];
            if (padding > 8) padding = 0;
        }
        else {
            padding = 0;
        }
        StringAppend(s, processed_block);
    }

    return s;
}

void substring(const char *src, int index, int len, char *target)
{
    int i;
    for (i = 0; i < len; i++) {
        target[i] = src[index + i];
        if (target[i] == '\0') break;
    }
}

void StringAppend(char *des, const char src[8])
{
    int i, j;
    j = 0;
    while (*(des + j) != '\0') j++;
    
    for (i = 0; i < 8; i++) 
        *(des + j + i) = src[i];
    *(des + j + i) = 0;
}
